<!DOCTYPE html>
<html>
<head>
    <title>大球套小球</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // 初始化场景、相机和渲染器
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(15, window.innerWidth / window.innerHeight, 1, 2000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 加载字体
        const fontLoader = new THREE.FontLoader();
        let font;
        fontLoader.load('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json', function(loadedFont) {
            font = loadedFont;
        });

        // 创建带文字的材质
        function createTexturedMaterial(color, text) {
            const canvas = document.createElement('canvas');
            const context = canvas.getContext('2d');
            canvas.width = 512;
            canvas.height = 512;

            // 绘制背景色
            context.fillStyle = `rgba(${(color >> 16) & 0xFF}, ${(color >> 8) & 0xFF}, ${color & 0xFF}, 0.8)`;
            context.fillRect(0, 0, canvas.width, canvas.height);

            // 设置文字样式
            context.font = 'bold 80px "Microsoft YaHei", Arial, sans-serif'; // 减小字体大小
            context.fillStyle = 'white';
            context.textAlign = 'center';
            context.textBaseline = 'middle';

            // 分别绘制两个字，调整位置
            const charWidth = 80; // 估计每个字的宽度
            context.fillText(text[0], canvas.width / 2 - charWidth / 2, canvas.height / 2);
            context.fillText(text[1], canvas.width / 2 + charWidth / 2, canvas.height / 2);

            // 创建纹理
            const texture = new THREE.CanvasTexture(canvas);
            texture.needsUpdate = true;

            // 创建材质
            return new THREE.MeshPhongMaterial({
                map: texture,
                transparent: true,
                opacity: 0.8
            });
        }

        // 创建大球
        const outerGeometry = new THREE.SphereGeometry(12, 32, 32);
        const outerMaterial = new THREE.MeshPhongMaterial({
            color: 0x00ff00,
            transparent: true,
            opacity: 0.3,
            wireframe: true
        });
        const outerSphere = new THREE.Mesh(outerGeometry, outerMaterial);
        scene.add(outerSphere);

        // 创建小球
        const innerGeometry = new THREE.SphereGeometry(6, 32, 32);
        const innerMaterial = new THREE.MeshPhongMaterial({
            color: 0xff0000,
            transparent: true,
            opacity: 0.8
        });
        const innerSphere = new THREE.Mesh(innerGeometry, innerMaterial);
        scene.add(innerSphere);

        // 创建圆环材质
        const ringMaterial = new THREE.MeshPhongMaterial({
            color: 0xffff00,
            transparent: true,
            opacity: 0.8,
            wireframe: true
        });

        // 创建中间大圆环
        const middleRingGeometry = new THREE.TorusGeometry(7.2, 0.32, 16, 100);
        const middleRing = new THREE.Mesh(middleRingGeometry, ringMaterial);
        middleRing.rotation.x = Math.PI / 2; // 水平放置
        scene.add(middleRing);

        // 创建上方小圆环
        const topRingGeometry = new THREE.TorusGeometry(6, 0.32, 16, 100);
        const topRing = new THREE.Mesh(topRingGeometry, ringMaterial);
        topRing.rotation.x = Math.PI / 2; // 水平放置
        topRing.position.y = 3; // 向上移动
        scene.add(topRing);

        // 创建上方圆环上的小球
        const smallSpheres = [];
        for (let i = 0; i < 3; i++) {
            const angle = (i * 2 * Math.PI) / 3 + Math.PI / 2;
            const smallSphereGeometry = new THREE.SphereGeometry(1.5, 16, 16);
            const smallSphereMaterial = createTexturedMaterial(0x00ffff, '你好');
            const smallSphere = new THREE.Mesh(smallSphereGeometry, smallSphereMaterial);
            
            // 计算小球在圆环上的位置
            const radius = 6;
            let x = Math.cos(angle) * radius;
            let z = Math.sin(angle) * radius;
            
            smallSphere.position.x = x;
            smallSphere.position.z = z;
            smallSphere.position.y = 0;
            
            // 将小球添加到场景中
            scene.add(smallSphere);
            smallSpheres.push(smallSphere);
        }

        // 创建下方小圆环
        const bottomRingGeometry = new THREE.TorusGeometry(6, 0.32, 16, 100);
        const bottomRing = new THREE.Mesh(bottomRingGeometry, ringMaterial);
        bottomRing.rotation.x = Math.PI / 2; // 水平放置
        bottomRing.position.y = -3; // 向下移动
        scene.add(bottomRing);

        // 添加光源
        const light = new THREE.DirectionalLight(0xffffff, 1);
        light.position.set(28, 28, 28);
        scene.add(light);

        const ambientLight = new THREE.AmbientLight(0x404040);
        scene.add(ambientLight);

        // 设置相机位置
        camera.position.set(0, 45, 110); // 调整相机位置，让视角更小，为环绕文字留出空间
        camera.lookAt(0, 0, 0);

        // 创建环绕文字
        function createCircularText(text, radius, yOffset) {
            const textGroup = new THREE.Group();
            const charWidth = 2; // 每个字的宽度
            const totalWidth = charWidth * text.length;
            const startX = -totalWidth / 2;

            // 为每个字符创建Sprite
            for (let i = 0; i < text.length; i++) {
                // 创建canvas
                const canvas = document.createElement('canvas');
                const context = canvas.getContext('2d');
                canvas.width = 128;
                canvas.height = 128;

                // 设置文字样式
                context.font = 'bold 80px "Microsoft YaHei", Arial, sans-serif';
                context.fillStyle = 'white';
                context.textAlign = 'center';
                context.textBaseline = 'middle';

                // 绘制文字
                context.fillText(text[i], canvas.width / 2, canvas.height / 2);

                // 创建纹理
                const texture = new THREE.CanvasTexture(canvas);
                texture.needsUpdate = true;

                // 创建Sprite材质
                const spriteMaterial = new THREE.SpriteMaterial({
                    map: texture,
                    transparent: true,
                    opacity: 0.8
                });

                // 创建Sprite
                const sprite = new THREE.Sprite(spriteMaterial);
                sprite.scale.set(2, 2, 1);

                // 计算字符位置，使其在球体顶部对称弯曲排列
                const x = startX + i * charWidth;
                const y = yOffset;
                // 使用正弦函数计算z坐标，使文字对称弯曲
                const normalizedX = (x - startX) / totalWidth; // 将x坐标归一化到[0,1]范围
                const z = Math.sin(normalizedX * Math.PI) * radius; // 使用π作为周期，确保对称

                sprite.position.set(x, y, z);
                
                // 计算球面法向量
                const normal = new THREE.Vector3(x, y, z).normalize();
                // 计算切线方向（垂直于法向量和y轴）
                const tangent = new THREE.Vector3().crossVectors(normal, new THREE.Vector3(0, 1, 0)).normalize();
                // 计算切线旋转角度
                const angle = Math.atan2(tangent.z, tangent.x);
                // 应用旋转
                sprite.rotation.y = angle;
                
                // 让文字始终面向相机
                sprite.material.depthTest = false;
                sprite.renderOrder = 1;

                textGroup.add(sprite);
            }

            return textGroup;
        }

        // 创建环绕文字
        const circularText = createCircularText('三点一线协调机制', 12, 12);
        scene.add(circularText);

        // 动画循环
        function animate() {
            requestAnimationFrame(animate);

            // 旋转大球
            outerSphere.rotation.x += 0.005;
            outerSphere.rotation.y += 0.005;

            // 旋转小球
            innerSphere.rotation.x += 0.01;
            innerSphere.rotation.y += 0.01;

            // 更新小球位置
            for (let i = 0; i < 3; i++) {
                const smallSphere = smallSpheres[i];
                const angle = (i * 2 * Math.PI) / 3 + Math.PI / 2;
                const radius = 6;
                let x = Math.cos(angle) * radius;
                let z = Math.sin(angle) * radius;
                
                smallSphere.position.x = x;
                smallSphere.position.z = z;
                smallSphere.position.y = topRing.position.y;
                
                // 让小球保持固定方向，不旋转
                smallSphere.rotation.set(0, 0, 0);
            }

            // 让文字组始终面向相机
            if (circularText) {
                circularText.quaternion.copy(camera.quaternion);
            }

            renderer.render(scene, camera);
        }
        animate();

        // 响应窗口大小变化
        window.addEventListener('resize', onWindowResize, false);
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }
    </script>
</body>
</html> 